package com.yunyouyi.cpicture.domain.picture.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yunyouyi.cpicture.domain.picture.entity.Picture;
import com.yunyouyi.cpicture.domain.picture.repository.PictureRepository;
import com.yunyouyi.cpicture.domain.picture.service.PictureDomainService;
import com.yunyouyi.cpicture.domain.user.entity.User;
import com.yunyouyi.cpicture.infrastructure.api.CosManager;
import com.yunyouyi.cpicture.infrastructure.api.aliyunai.AliYunAiApi;
import com.yunyouyi.cpicture.infrastructure.api.aliyunai.model.CreateOutPaintingTaskRequest;
import com.yunyouyi.cpicture.infrastructure.api.aliyunai.model.CreateOutPaintingTaskResponse;
import com.yunyouyi.cpicture.infrastructure.exception.BusinessException;
import com.yunyouyi.cpicture.infrastructure.exception.ErrorCode;
import com.yunyouyi.cpicture.infrastructure.exception.ThrowUtils;
import com.yunyouyi.cpicture.interfaces.dto.picture.*;
import com.yunyouyi.cpicture.interfaces.vo.picture.PictureVO;
import com.yunyouyi.cpicture.infrastructure.manager.upload.FilePictureUpload;
import com.yunyouyi.cpicture.infrastructure.manager.upload.PictureUploadTemplet;
import com.yunyouyi.cpicture.infrastructure.manager.upload.UrlPictureUpload;
import com.yunyouyi.cpicture.infrastructure.manager.upload.model.dto.file.UploadPictureDTO;
import com.yunyouyi.cpicture.domain.space.entity.Space;
import com.yunyouyi.cpicture.domain.picture.valueobject.PictureReviewStatusEnum;
import com.yunyouyi.cpicture.application.service.SpaceApplicationService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
* @author yunyouyi
* @description 针对表【picture(图片)】的数据库操作Service实现
* @createDate 2025-04-04 10:55:47
*/
@Service
@Slf4j
public class PictureDomainServiceImpl
    implements PictureDomainService {

    @Resource
    private PictureRepository pictureRepository;

    @Resource
    private FilePictureUpload filePictureUpload;

    @Resource
    private UrlPictureUpload urlPictureUpload;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Autowired
    private CosManager cosManager;

    @Resource
    private SpaceApplicationService spaceApplicationService;

    @Resource
    private AliYunAiApi aliYunAiApi;

    /**
     * 上传图片
     * @param inputSource
     * @param pictureUploadDTO
     * @param loginUser
     * @return 图片封装类
     */
    public PictureVO uploadPicture(Object inputSource, PictureUploadDTO pictureUploadDTO, User loginUser) {
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR,"请登录");
        ThrowUtils.throwIf(pictureUploadDTO == null,ErrorCode.PARAMS_ERROR,"图片信息不能为空");
        //TODO检验空间
        Long spaceId = pictureUploadDTO.getSpaceId();
        if(spaceId != null){
            Space space = spaceApplicationService.getById(spaceId);
            ThrowUtils.throwIf(space == null,ErrorCode.PARAMS_ERROR,"空间不存在");
            //查看是否为管理员或者本人
            ThrowUtils.throwIf(!space.getUserId().equals(loginUser.getId()) && !loginUser.isAdmin(),ErrorCode.NO_AUTH_ERROR,"无权限");
            //检验空间的额度
            ThrowUtils.throwIf(space.getTotalSize()> space.getMaxSize(),ErrorCode.PARAMS_ERROR,"空间剩余容量不足");
            ThrowUtils.throwIf(space.getTotalCount()> space.getMaxCount(),ErrorCode.PARAMS_ERROR,"空间剩余条数不足");
        }
        //判断新增还是更新图片
        Long pictureId = null;
        pictureId = pictureUploadDTO.getId();
        //如果是更新校验图片是否存在
        if (pictureId != null){
            Picture oldPicture = pictureRepository.getById(pictureId);
            ThrowUtils.throwIf(oldPicture == null,ErrorCode.NOT_FOUND_ERROR,"图片不存在");
            //权限判断
            ThrowUtils.throwIf(!oldPicture.getUserId().equals(loginUser.getId()) && !loginUser.isAdmin(),ErrorCode.NO_AUTH_ERROR,"无权限");
            //判断有没有spaceId
            if(spaceId == null){
                if(oldPicture.getSpaceId() != null){
                    spaceId = oldPicture.getSpaceId();
                }
            }
            //更新时空间Id不能改变
            if(spaceId != null){
                ThrowUtils.throwIf(!spaceId.equals(oldPicture.getSpaceId()),ErrorCode.NO_AUTH_ERROR,"空间id不一致");
            }
        }
        //图片上传得到信息
        String uploadPathPrefix;
        if(spaceId != null){
            uploadPathPrefix =  String.format("space/%s",spaceId);
        }else{
            uploadPathPrefix =  String.format("public/%s",loginUser.getId());
        }
        //根据文件inputSource类型选择上传方式
        PictureUploadTemplet picUploadTemplet = filePictureUpload;
        if(inputSource instanceof String){
            picUploadTemplet = urlPictureUpload;
        }
        UploadPictureDTO uploadPictureDTO = picUploadTemplet.uploadPicture(inputSource,uploadPathPrefix);
        //构造图片信息
        Picture picture = new Picture();
        BeanUtils.copyProperties(uploadPictureDTO,picture);
        //传来的信息无图片名则指定
        String picName = uploadPictureDTO.getPicName();
        if (StrUtil.isNotBlank(pictureUploadDTO.getPicName())){
            picName = pictureUploadDTO.getPicName();
        }
        picture.setSpaceId(spaceId);
        picture.setThumbnailUrl(uploadPictureDTO.getThumbnailUrl());
        picture.setName(picName);
        picture.setUserId(loginUser.getId());
        //填充审核信息
        this.fileReviewParam(picture,loginUser);
        log.info("构造后的图片信息{}", picture);
        //如果pictureId为空则为新增图片
        if(pictureId != null){
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        }
        //开启事务
        Long finalSpaceId = spaceId;
        transactionTemplate.execute(status -> {
            boolean result = pictureRepository.saveOrUpdate(picture);
            ThrowUtils .throwIf(!result,ErrorCode.OPERATION_ERROR,"图片上传失败");
            if(finalSpaceId != null){
                boolean update = spaceApplicationService.lambdaUpdate()
                        .eq(Space::getId, finalSpaceId)
                        .setSql("totalSize = totalSize + " + picture.getPicSize())
                        .setSql("totalCount = totalCount + 1")
                        .update();
                ThrowUtils.throwIf(!update,ErrorCode.OPERATION_ERROR,"空间额度更新失败");
            }
            return picture;
        });
        return PictureVO.obj2Vo(picture);
    }

    /**
     * 删除图片
     * @param pictureId
     * @param loginUser
     */
    public boolean deletePicture(Long pictureId, User loginUser) {
        ThrowUtils.throwIf(pictureId <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        // 判断是否存在
        Picture oldPicture = pictureRepository.getById(pictureId);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        log.info("spaceId = {}",oldPicture.getSpaceId());
        // 校验图片的操作权限
        //改为使用注解鉴权checkPictureAuth(oldPicture, loginUser);
        if(oldPicture.getSpaceId() != null){
            //开启事务
            transactionTemplate.execute(status -> {
                // 操作数据库
                boolean result = pictureRepository.removeById(pictureId);
                ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
                // 清除额度
                boolean update = spaceApplicationService.lambdaUpdate()
                        .eq(Space::getId, oldPicture.getSpaceId())
                        .setSql("totalSize = totalSize - " + oldPicture.getPicSize())
                        .setSql("totalCount = totalCount - 1")
                        .update();
                ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR,"额度清除失败");
                return true;
            });
        }else{
            // 操作数据库
            boolean result = pictureRepository.removeById(pictureId);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        }
        //清理图片
        this.clearPictureFile(oldPicture);
        return true;
    }

    /**
     * 获取查询包装类
     * @param pictureQueryDTO
     * @return QueryWrapper
     */
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryDTO pictureQueryDTO) {
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        if (pictureQueryDTO == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = pictureQueryDTO.getId();
        String name = pictureQueryDTO.getName();
        String introduction = pictureQueryDTO.getIntroduction();
        String category = pictureQueryDTO.getCategory();
        List<String> tags = pictureQueryDTO.getTags();
        Long picSize = pictureQueryDTO.getPicSize();
        Integer picWidth = pictureQueryDTO.getPicWidth();
        Integer picHeight = pictureQueryDTO.getPicHeight();
        Double picScale = pictureQueryDTO.getPicScale();
        String picFormat = pictureQueryDTO.getPicFormat();
        String searchText = pictureQueryDTO.getSearchText();
        Long userId = pictureQueryDTO.getUserId();
        String sortField = pictureQueryDTO.getSortField();
        String sortOrder = pictureQueryDTO.getSortOrder();
        Long spaceId = pictureQueryDTO.getSpaceId();
        boolean nullSpaceId = pictureQueryDTO.isNullSpaceId();
        Date startTime = pictureQueryDTO.getStartEditTime();
        Date endTime = pictureQueryDTO.getEndEditTime();
        //审核字段
        Integer reviewStatus = pictureQueryDTO.getReviewStatus();
        String reviewMessage = pictureQueryDTO.getReviewMessage();
        Long reviewerId = pictureQueryDTO.getReviewerId();
        Date reviewTime = pictureQueryDTO.getReviewTime();
        // 从多字段中搜索
        if (StrUtil.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("name", searchText)
                    .or()
                    .like("introduction", searchText)
            );
        }
        //日期
        queryWrapper.ge(ObjUtil.isNotEmpty(startTime), "editTime", startTime);
        queryWrapper.lt(ObjUtil.isNotEmpty(endTime), "editTime", endTime);
        //其他
        queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjUtil.isNotEmpty(spaceId), "spaceId", spaceId);
        queryWrapper.isNull(nullSpaceId, "spaceId");//当nullSpaceId为null查询空值
        queryWrapper.like(StrUtil.isNotBlank(name), "name", name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        queryWrapper.like(StrUtil.isNotBlank(picFormat), "picFormat", picFormat);
        queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);
        queryWrapper.eq(ObjUtil.isNotEmpty(picWidth), "picWidth", picWidth);
        queryWrapper.eq(ObjUtil.isNotEmpty(picHeight), "picHeight", picHeight);
        queryWrapper.eq(ObjUtil.isNotEmpty(picSize), "picSize", picSize);
        queryWrapper.eq(ObjUtil.isNotEmpty(picScale), "picScale", picScale);
        //审核字段查询
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewStatus), "reviewStatus", reviewStatus);
        queryWrapper.like(StrUtil.isNotBlank(reviewMessage), "reviewMessage", reviewMessage);
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewerId), "reviewerId", reviewerId);
        // JSON 数组查询
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // 排序
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }
    /**
     * 图片审核
     * @param pictureReviewDTO
     * @param loginUser
     * @return
     */
    public boolean doPictureReview(PictureReviewDTO pictureReviewDTO, User loginUser) {
        //校验信息
        ThrowUtils.throwIf(pictureReviewDTO==null,ErrorCode.PARAMS_ERROR);
        Long id = pictureReviewDTO.getId();
        Integer reviewStatus = pictureReviewDTO.getReviewStatus();
        //封装
        PictureReviewStatusEnum reviewEnum = PictureReviewStatusEnum.getEnumByValue(reviewStatus);

        String reviewMessage = pictureReviewDTO.getReviewMessage();
        ThrowUtils.throwIf(id==null||reviewEnum==null||PictureReviewStatusEnum.REVIEWING.equals(reviewEnum),ErrorCode.PARAMS_ERROR);
        //判断图片是否存在
        Picture oldPicture = pictureRepository.getById(id);
        ThrowUtils.throwIf(oldPicture==null,ErrorCode.NOT_FOUND_ERROR);
        //校验审核状态是否重复
        ThrowUtils.throwIf(oldPicture.getReviewStatus().equals(reviewStatus),ErrorCode.PARAMS_ERROR,"请勿重复审核");
        //数据库操作
        Picture picture = new Picture();
        BeanUtils.copyProperties(pictureReviewDTO,picture);
        picture.setReviewerId(loginUser.getId());
        picture.setReviewTime(new Date());
        if(pictureRepository.updateById(picture)){
            return true;
        }else {
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
    }
    /**
     * 填充审核参数
     * @param picture
     * @param loginUser
     */
    public void fileReviewParam(Picture picture, User loginUser){
        if (loginUser.isAdmin()){
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewMessage("管理员自动过审核");
            picture.setReviewTime(new Date());
            picture.setReviewerId(loginUser.getId());
        }else{
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }
    }
    /**
     * 批量上传图片
     * @param pictureUploadByBatchDTO
     * @param loginUser
     * @return 成功上传数量
     */
    public Integer uploadPictureByBatch(PictureUploadByBatchDTO pictureUploadByBatchDTO, User loginUser) {
        String searchText = pictureUploadByBatchDTO.getSearchText();
        Integer count = pictureUploadByBatchDTO.getCount();
        String namePrefix = pictureUploadByBatchDTO.getNamePrefix();
        if(namePrefix.isEmpty()){
            namePrefix = searchText;
        }
        ThrowUtils.throwIf(StrUtil.isBlank(searchText),ErrorCode.PARAMS_ERROR,"搜索内容不能为空");
        int uploadedCount = 0;
        //限制抓取数量
        ThrowUtils.throwIf(count>20,ErrorCode.PARAMS_ERROR,"最多上传20张图片");
        //抓取的地址
        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1",searchText);
        log.info("开始抓取地址{}",fetchUrl);
        Document doc;
        try {
            doc = Jsoup.connect(fetchUrl).get();
        } catch (IOException e) {
            log.info("抓取失败doc获取错误",e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "抓取失败doc获取失败");
        }
        Element div = doc.getElementsByClass("dgControl").first();
        ThrowUtils.throwIf(ObjUtil.isNull(div), ErrorCode.OPERATION_ERROR, "抓取失败没有找到dgControl");
        Elements elements = div.select("img.mimg");
        for(Element element : elements){
            String src = element.attr("src");
            if(StrUtil.isBlank(src)){
                log.info("图片地址为空,跳过{}",src);
            }
            log.info("图片地址src={}",src);
            //处理链接
            int index = src.indexOf("?");
            if (index > -1){
                src = src.substring(0,index);
            }
            log.info("处理后的src={}",src);
            //上传图片
            PictureUploadDTO pictureUploadDTO = new PictureUploadDTO();
            try {
                String name;
                name = String.format(namePrefix+(uploadedCount+1));
                log.info("namePrefix{}",name);
                //设置图片名称
                pictureUploadDTO.setPicName(name);
                //上传
                this.uploadPicture(src,pictureUploadDTO,loginUser);
                log.info("图片上传成功id={}", pictureUploadDTO.getId());
                uploadedCount++;
            } catch (Exception e) {
                log.info("图片上传失败",e);
            }
            if(uploadedCount >= count){
                break;
            }
        }
        return uploadedCount;
    }

    /**
     *  清理图片
     * @param oldPicture
     */
    @Async
    public void clearPictureFile(Picture oldPicture) {
        //判断图片是否被多条记录使用
        String picUrl = oldPicture.getUrl();
        long cnt = pictureRepository.lambdaQuery().eq(Picture::getUrl, picUrl).count();
        if (cnt > 1){
            return;
        }
        //删除图片
        cosManager.deleteObject(picUrl);
        //删除缩略图
        String thumbnailUrl = oldPicture.getThumbnailUrl();
        if (StrUtil.isNotBlank(thumbnailUrl)){
            cosManager.deleteObject(thumbnailUrl);
        }
    }

    /**
     * 检查图片的操作权限
     * @param picture
     * @param loginUser
     */
    @Override
    public void checkPictureAuth(Picture picture, User loginUser) {
        Long spaceId = picture.getSpaceId();
        if(spaceId != null){
            Space space = spaceApplicationService.getById(spaceId);
            ThrowUtils.throwIf(space == null,ErrorCode.PARAMS_ERROR,"空间不存在");
            //查看是否为本人
            ThrowUtils.throwIf(!space.getUserId().equals(loginUser.getId()),ErrorCode.NO_AUTH_ERROR,"无权限");
        }else{
            //公共图库及管理和本人可以操作
            ThrowUtils.throwIf(!loginUser.isAdmin() && !loginUser.getId().equals(picture.getUserId()),ErrorCode.NO_AUTH_ERROR,"无权限");
        }
    }

    /**
     * tags和分类批量更新
     * @param pictureEditByBatchDTO
     * @param loginUser
     */
    @Override
    @Transactional
    public void editPictureByBatch(PictureEditByBatchDTO pictureEditByBatchDTO, User loginUser){

        List<Long> pictureIdList = pictureEditByBatchDTO.getPictureIdList();
        Long spaceId = pictureEditByBatchDTO.getSpaceId();
        String category = pictureEditByBatchDTO.getCategory();
        List<String> tags = pictureEditByBatchDTO.getTags();

        // 1.校验数据
        ThrowUtils.throwIf(CollectionUtil.isEmpty(pictureIdList),ErrorCode.PARAMS_ERROR,"图片id不能为空");
        ThrowUtils.throwIf(loginUser == null,ErrorCode.NOT_LOGIN_ERROR,"未登录");

        // 2.空间权限校验
        Space space = spaceApplicationService.getById(spaceId);
        ThrowUtils.throwIf(space == null,ErrorCode.PARAMS_ERROR,"空间不存在");
        ThrowUtils.throwIf(!space.getUserId().equals(loginUser.getId()),ErrorCode.NO_AUTH_ERROR,"无权限");

        // 3.批量修改图片
        List<Picture> pictureList = pictureRepository.lambdaQuery()
                .select(Picture::getId, Picture::getSpaceId, Picture::getUserId)
                .in(Picture::getId, pictureIdList)
                .eq(Picture::getSpaceId, spaceId)
                .list();
        ThrowUtils.throwIf(CollectionUtil.isEmpty(pictureList),ErrorCode.PARAMS_ERROR,"图片不存在");
        pictureList.forEach(picture -> {
            if(StrUtil.isNotBlank(category)){
                picture.setCategory(category);
            }
            if(CollUtil.isNotEmpty(tags)){
                picture.setTags(JSONUtil.toJsonStr(tags));
            }
            // 批量重命名
            String nameRule = pictureEditByBatchDTO.getNameRule();
            fillPictureWithNameRule(pictureList, nameRule);
        });
        boolean result = pictureRepository.updateBatchById(pictureList);
        ThrowUtils.throwIf(!result,ErrorCode.OPERATION_ERROR,"修改失败");
    }

    /**
     * 创建扩图任务
     *
     * @param createPictureOutPaintingTaskDTO
     * @param loginUser
     * @return
     */
    @Override
    public CreateOutPaintingTaskResponse createPictureOutPainting(CreatePictureOutPaintingTaskDTO createPictureOutPaintingTaskDTO, User loginUser){
        Long pictureId = createPictureOutPaintingTaskDTO.getPictureId();
        //获取图片
        Picture picture = pictureRepository.getById(pictureId);
        ThrowUtils.throwIf(picture == null,ErrorCode.PARAMS_ERROR,"图片不存在");
        //校验权限
        //改为使用注解鉴权 checkPictureAuth(picture, loginUser);
        //创建扩图任务
        CreateOutPaintingTaskRequest createOutPaintingTaskRequest = new CreateOutPaintingTaskRequest();
        //设置输入图片
        CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
        input.setImageUrl(picture.getUrl());
        createOutPaintingTaskRequest.setInput(input);
        //设置参数
        createOutPaintingTaskRequest.setParameters(createPictureOutPaintingTaskDTO.getParameters());
        //创建任务
        CreateOutPaintingTaskResponse createOutPaintingTaskResponse = aliYunAiApi.createOutPaintingTask(createOutPaintingTaskRequest);
        return createOutPaintingTaskResponse;
    }
    /**
     * nameRule 格式：图片{序号}
     * @param pictureList
     * @param nameRule
     */
    private void fillPictureWithNameRule(List<Picture> pictureList, String nameRule) {
        if (CollUtil.isEmpty(pictureList) || StrUtil.isBlank(nameRule)) {
            return;
        }
        long count = 1;
        try {
            for (Picture picture : pictureList) {
                String pictureName = nameRule.replaceAll("\\{序号}", String.valueOf(count++));
                picture.setName(pictureName);
            }
        } catch (Exception e) {
            log.error("名称解析错误", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "名称解析错误");
        }
    }
}









